package chat

import (
	"container/list"
	"net/http"

	log "github.com/cihub/seelog"
	"github.com/julienschmidt/httprouter"
	"golang.org/x/net/websocket"
)

const (
	maxPastMessageNum = 100
)

// Chat Server Instance
var ChatServer *Server

// Chat server.
type Server struct {
	path string
	// past messages
	messages *list.List
	// all client
	clients   map[int]*Client
	addCh     chan *Client
	delCh     chan *Client
	sendAllCh chan *Message
	doneCh    chan bool
	errCh     chan error
}

// Create new chat server.
func NewServer(pattern string) *Server {
	messages := list.New()
	clients := make(map[int]*Client)
	addCh := make(chan *Client)
	delCh := make(chan *Client)
	sendAllCh := make(chan *Message)
	doneCh := make(chan bool)
	errCh := make(chan error)

	return &Server{pattern, messages, clients, addCh, delCh, sendAllCh, doneCh, errCh}
}

func (s *Server) Add(c *Client) {
	s.addCh <- c
}

func (s *Server) Del(c *Client) {
	s.delCh <- c
}

func (s *Server) SendAll(msg *Message) {
	s.sendAllCh <- msg
}

func (s *Server) Done() {
	s.doneCh <- true
}

func (s *Server) Err(err error) {
	s.errCh <- err
}

func (s *Server) sendPastMessages(c *Client) {
	for element := s.messages.Front(); element != nil; element = element.Next() {
		msg, ok := element.Value.(Message)
		if ok {
			c.Write(&msg)
		}
	}
}

func (s *Server) sendAll(msg *Message) {
	for _, c := range s.clients {
		c.Write(msg)
	}
}

// Listen and serve.
// It serves client connection and broadcast request.
func (s *Server) Listen(router *httprouter.Router) {
	log.Info("Listening websocket server...")

	// websocket handler
	onConnected := func(ws *websocket.Conn) {
		defer func() {
			err := ws.Close()
			if err != nil {
				s.errCh <- err
			}
		}()

		// The request
		// ws.Request()

		client := NewClient(ws, s)
		s.Add(client)
		client.Listen()
	}

	// Http router adapter
	// http.Handle(s.path, websocket.Handler(onConnected))
	warpper := func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		websocket.Handler(onConnected).ServeHTTP(w, r)
	}
	router.GET(s.path, warpper)
	log.Info("Created websocket handler.")

	for {
		select {
		// Add new a client
		case c := <-s.addCh:
			s.clients[c.id] = c
			log.Infof("Added new client, now %d clients connected.", len(s.clients))
			s.sendPastMessages(c)

		// del a client
		case c := <-s.delCh:
			delete(s.clients, c.id)
			log.Infof("Delete client, now %d clients connected.", len(s.clients))

		// broadcast message for all clients
		case msg := <-s.sendAllCh:
			// save recent message
			if s.messages.Len() >= maxPastMessageNum {
				if element := s.messages.Front(); element != nil {
					s.messages.Remove(element)
				}
			}
			s.messages.PushBack(*msg)

			// broadcast to all clients
			s.sendAll(msg)

		case err := <-s.errCh:
			log.Error("Error: ", err.Error())

		case <-s.doneCh:
			return
		}
	}
}
